;;; init-org.el  -*- lexical-binding: t; -*-

(defvar colawithsauce/my-events '(("<2021-06-01>" "五月结束" 50)
                                 ("<2021-12-22>" "考研" 300)))

(defconst colawithsauce/quote-all-the-quote
  '(;; Ancient quotes
    "天下之事有难易乎？为之，则难者亦易矣；不为，则易者亦难矣。 —— 《为学》彭端淑"
    "世之所以因循苟且，随俗习非，而卒归于污下者，凡以志之弗立也。 —— 《示弟立志说》王阳明"
    "知而不行，只是未知。知而不行，便不真知。 —— 《传习录》王阳明"
    "人心惟危，道心惟微；惟精惟一，允执厥中。 —— 《尚书》"
    "桑榆已逝，东隅未晚 —— 《滕王阁序》王勃"
    "悔悟是去病之药；然以改之为贵，若留滞于中，则以因药发病。 —— 《传习录》王阳明"
    "人非圣贤，孰能无过？过而能改，善莫大焉！ —— 《左传》"
    ;; My quotes
    "君子闻过则喜"
    "即使题目做不出来，也应该将思路写下来以对照答案思路，否则做这道题目的时间就被浪费掉了。"
    ;; English quotes
    "We all products of our time.")
  "All the quote sentences.")

(with-eval-after-load 'org-capture
  ;; Customize capture templates
  (setq org-directory "~/Notes/Captures"
        +personal-life-list-file (expand-file-name "Life_list.org" org-directory)
        +proverbs-file (expand-file-name "word-and-phrases.org" org-directory)
        +org-capture-todo-file (expand-file-name "todo.org" org-directory)
        +org-capture-notes-file (expand-file-name "notes.org" org-directory)
        +org-capture-journal-file (expand-file-name "journal.org" org-directory))

  (dolist
      (template
       '(("t" "Personal todo" entry
          (file+headline +org-capture-todo-file "Inbox")
          "* [!] %?" :prepend t)
         ;; I had switch it to roam!
         ;; ("n" "Personal notes" entry
         ;;  (file+headline +org-capture-notes-file "Inbox")
         ;;  "* %<%R> %?" :prepend t)

         ;; Not useful, I don't have somany items need capture.
         ;; ("l" "Personal Life List" entry
         ;;  (file+headline +personal-life-list-file "All")
         ;;  "* [!] %?\n" :prepend t)

         ;; I had never use it.
         ;; ("w" "proverbs" entry
         ;;  (file+headline +proverbs-file "all")
         ;;  "* %?" :prepend t)

         ;; Move Journal to Roam.
         ;; ("j" "Journal")
         ;; ("jj" "Daily Journal" entry
         ;;  (file+olp+datetree +org-capture-journal-file)
         ;;  "* %<%R> %?\n")
         ;; ("je" "Revise Journal" entry
;;           (file+olp+datetree "reviseJournal.org")
;;           "* %<%R> %^{Item Name}
;; %?
;; " :tree-type week :unnarrowed t :clock-in t :clock-resume t)
         ;; ("jp" "Revise Plan" entry
         ;;  (file+olp+datetree "revisePlan.org")
         ;;  "%[~/.emacs.d/templates/Plan.txt]"
         ;;  :tree-type month)
         ))
    (add-to-list 'org-capture-templates template))
  ;; Populates only the EXPORT_FILE_NAME property in the inserted headline.
  (defun org-hugo-new-subtree-post-capture-template ()
    "Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."
    (let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
           (fname (org-hugo-slug title)))
      (mapconcat #'identity
                 `(
                   ,(concat "* DRAFT " title)
                   ":PROPERTIES:"
                   ,(concat ":EXPORT_FILE_NAME: " fname)
                   ":END:"
                   "%?\n")              ;Place the cursor here finally
                 "\n")))
  (dolist
      (templtes
       '(("h" "Hugo Post")
         ("hr"
          "Reading"
          entry
          (file+olp "~/Notes/Blog/all-posts.org" "Reading")
          (function org-hugo-new-subtree-post-capture-template)
          :clock-in t :clock-resume t)
         ("ha"
          "Article"
          entry
          (file+olp "~/Notes/Blog/all-posts.org" "Article")
          (function org-hugo-new-subtree-post-capture-template)
          :clock-in t :clock-resume t)
         ("hn"
          "Note"
          entry
          (file+olp "~/Notes/Blog/all-posts.org" "Note")
          (function org-hugo-new-subtree-post-capture-template)
          :clock-in t :clock-resume t)

         ("he"
          "考研"
          entry
          (file+olp "~/Notes/Blog/all-posts.org" "考研")
          "* DRAFT %<%Y-%m-%d>考研周记
:PROPERTIES:
:EXPORT_FILE_NAME: %<%Y-%m-%d>
:END:
%(colawithsauce/remain-days-show-all)
#+HUGO: more\n
%?\n" :clock-in t :clock-resume t)))
    (add-to-list 'org-capture-templates templtes))

  (global-set-key (kbd "C-c c") 'org-capture)
  (global-set-key (kbd "C-c a") 'org-agenda))

(with-eval-after-load 'org-agenda
  ;; variable pitch
  (add-hook 'org-agenda-mode-hook #'variable-pitch-mode)

  (custom-set-variables
  ;; org-agenda time grid
  '(org-agenda-time-grid
        '((daily today remove-match)
          (600 800 1000 1200 1400 1600 1800 2000 2200 2400)
          " >>>>>> " "------------------------------->>"))

  ;; Sorting strategy
  '(org-agenda-sorting-strategy
        '((agenda habit-down time-up ts-up
                  priority-down category-keep)
          (todo priority-down category-keep)
          (tags priority-down category-keep)
          (search category-keep)))

  ;; display diary file
  '(org-agenda-include-diary t)
  '(org-agenda-include-deadlines t)

  ;; location
  '(calendar-longitude 113.0)
  '(calendar-latitude 28.21)
   )

  ;; Sunrise and sunset
  (defun diary-sunrise ()
    (let ((dss (diary-sunrise-sunset)))
      (with-temp-buffer
        (insert dss)
        (goto-char (point-min))
        (while (re-search-forward " ([^)]*)" nil t)
          (replace-match "" nil nil))
        (goto-char (point-min))
        (search-forward ",")
        (buffer-substring (point-min) (match-beginning 0)))))

  (defun diary-sunset ()
    (let ((dss (diary-sunrise-sunset))
          start end)
      (with-temp-buffer
        (insert dss)
        (goto-char (point-min))
        (while (re-search-forward " ([^)]*)" nil t)
          (replace-match "" nil nil))
        (goto-char (point-min))
        (search-forward ", ")
        (setq start (match-end 0))
        (search-forward " at")
        (setq end (match-beginning 0))
        (goto-char start)
        (capitalize-word 1)
        (buffer-substring start end))))

  ;; Additional functions
  ;; (use-package org-super-agenda
  ;;   :defer t
  ;;   :disabled t
  ;;   :custom
  ;;   (org-super-agenda-group-property-name "TODO_TYPE")
  ;;   (org-super-agenda-groups
  ;;    '((:auto-group t :time-grid t)))
  ;;   )
  (use-package elegant-agenda-mode
    :defer t ;; Someday i would use it, but not now...
    )
  (use-package idle-org-agenda
    :after org-agenda
    :custom
    (idle-org-agenda-interval 600)
    (idle-org-agenda-key "a")
    :config (idle-org-agenda-mode))

  ;; Task reminding
  (defun colawithsauce/org-agenda-get-todos ()
    "Get todo and scheduled items until today."
    (let ((matched-items '())
          (print-result ""))
      (dolist (match
               (org-map-entries 'org-heading-components "SCHEDULED<=\"<today>\"/-DONE" 'agenda))
        (when (member (nth 2 match) '("[!]" "TODO" "[?]" "HACKING"))
          (push (concat "■ " (nth 4 match)) matched-items)))
      (while matched-items
        (setq print-result (concat (car matched-items) "\n" print-result))
        (setq matched-items (cdr matched-items)))
      (message print-result)))
  ;; Get quote
  (defun colawithsauce/quote-choice ()
  "Choice a piece of quote from the quote-list by random."
  (let ((quote-length (length colawithsauce/quote-all-the-quote)))
    (nth (random quote-length) colawithsauce/quote-all-the-quote)))

  (defun colawithsauce/org-agenda-write-file (&optional arg)
    "Write todo and scheduled items list to file. And write
to scratch buffer."
    ;; Write in file
    (with-temp-buffer
      (insert (colawithsauce/org-agenda-get-todos))
      (write-file "~/.task_today"))
    ;; Write in scratch buffer
    (with-current-buffer "*scratch*"
      (erase-buffer)
      (let* ((todo-list (butlast (split-string (colawithsauce/org-agenda-get-todos) "\n")))
             (todo-count (length todo-list)))
        (insert
         (format "%s\n" (make-string 70 ?\;))
         ";; ⏱Countdown:\n"
         (colawithsauce/remain-days-show-all ";;  - ")
         ";; 📓Quote:\n"
         (format ";;   - %s\n" (colawithsauce/quote-choice)))
        (if (not (equal todo-count 0))
            (progn (insert (format ";; ✅Tasks:\n" todo-count))
                   (while todo-list
                     (insert (format ";;   %s\n" (car todo-list)))
                     (setq todo-list (cdr todo-list))))
          (insert ";;; ✅Tasks all clear!\n"))
        (insert
         (format "%s\n" (make-string 70 ?\;)))
        (end-of-buffer))))

  (add-hook 'org-after-todo-state-change-hook 'colawithsauce/org-agenda-write-file)
  (add-hook 'after-init-hook 'colawithsauce/org-agenda-write-file)
  (add-hook 'after-make-frame-functions 'colawithsauce/org-agenda-write-file))

(with-eval-after-load 'org
  ;; Would Never use org-mode without following configuration

  ;; variable-pitch-mode
  (add-hook 'org-mode-hook 'variable-pitch-mode)

  ;; Set default picture display size
  (setq org-image-actual-width '(600))

  ;; Input latex with org-cdlatex
  (add-hook 'org-mode-hook 'org-cdlatex-mode)

  ;; Indent org file virtually
  (add-hook 'org-mode-hook 'org-indent-mode)

  ;; Do not indent on-the-fly
  (add-hook 'org-mode-hook (lambda () (electric-indent-local-mode -1)))

  ;; bind frequently used function
  (bind-key (kbd "C-c i") #'org-toggle-item org-mode-map)
  (bind-key [remap org-set-tags-command] #'counsel-org-tag org-mode-map)

  ;; visual line mode
  (add-hook 'org-mode-hook #'visual-line-mode)

  ;; Maybe we could have some integration
  (add-hook 'org-mode-hook (lambda () (company-mode -1)))

  ;; Org column font settings
  (set-face-font 'org-column (face-attribute 'variable-pitch :font))

  ;; Fix cdlatex's buggy behavior in inline latex snippet
  ;; (with-eval-after-load 'cdlatex
  ;;   ;; Fix org-cdlatex's behavior in org-mode latex fragment
  ;;   (define-advice texmathp (:before-until () org-cdlatex-fix)
  ;;     "In org-cdlatex-mode, call `org-inside-LaTeX-fragment-p'"
  ;;     (if org-cdlatex-mode
  ;;         (org-inside-LaTeX-fragment-p)
  ;;       nil))
  ;;   )

  (with-eval-after-load 'org-table
      ;; org table duration

    (custom-set-variables
     '(org-duration-format 'h:mm))
    )

  (with-eval-after-load 'org-clock
    ;; org-clock display settings
    (custom-set-variables
     '(org-clock-mode-line-total 'today))
    )

  ;;; My Functions
  (defun colawithsauce/org-insert-format-time-string ()
    "Insert formated time string"
    (interactive)
    (insert (format-time-string "%r")))

  (defun colawithsauce/remain-days-show (event-list &optional prefix suffix)
    "Show remain days to the DATE"
    (let ((date (pop event-list))
          (event (pop event-list))
          (quota (pop event-list))
          (prefix (or prefix nil))
          (suffix (or suffix "\n"))
          )
      (concat prefix "距 *" event "* 还有 "
       (int-to-string
        (org-time-stamp-to-now date)) " 天" suffix
       )))

  (defun colawithsauce/remain-days-show-all (&optional prefix suffix)
    "Show all remain days"
    (let ((result "")
          (prefix (or prefix nil))
          (suffix (or suffix "\n")))
      (dolist (event-list colawithsauce/my-events)
        (setq
         result
         (concat result (colawithsauce/remain-days-show event-list prefix suffix)))
        )
      (setq result (concat result))))

  (defun colawithsauce/remain-days-draw (event-list &optional prefix suffix)
  "Draw the text progress bar of the event"
  (let* ((date (pop event-list))
         (event (pop event-list))
         (quota (pop event-list))
         (prefix (or prefix nil))
         (suffix (or suffix "\n"))
         (total-num 50)
         (percentage (* (org-time-stamp-to-now date) (/ (float total-num) quota)))
         (draw-num (floor (/ percentage 2))))
    (concat prefix
            "["
            (make-string draw-num ?#)
            (make-string (- total-num draw-num) ?-)
            "]"
            (format " %%%.2f %s" percentage event)
            suffix)))

  (defun colawithsauce/remain-days-draw-all (&optional prefix suffix)
    "Draw all remain days"
    (let ((result ""))
      (dolist (event-list colawithsauce/my-events)
        (setq
         result
         (concat result (colawithsauce/remain-days-draw event-list prefix suffix))))
      result))

  (defun colawithsauce/org-screenshot ()
    "Take a screenshot and insert it."
    (interactive)
    (let ((filename
           (concat
            (make-temp-name
             (concat
              (file-name-directory (buffer-file-name))
              ".orgscr/"
              (file-name-base (buffer-file-name))
              "_"
              (format-time-string "%Y%m%d_%H%M%S_")) ) ".png")))
      (if (not (file-exists-p (file-name-directory filename)))
          (call-process "mkdir" nil nil nil ".orgscr"))
      (call-process "scrot" nil nil nil "-s" filename)
      (if (file-exists-p filename)
          (progn
            (insert "#+ATTR_ORG: :width 600") (newline-and-indent)
            (insert "#+ATTR_LATEX: :width 400") (newline-and-indent)
            (insert (concat "[[" filename "]]"))
            )
        (message "Can't find screenshot to insert!"))
      (org-display-inline-images)))

  (defun colawithsauce/org-insert-inline-formula ()
    "Insert fomular."
    (interactive)
    (insert "\\(  \\)")
    (backward-char 3))

  (defun colawithsauce/org-insert-formula ()
    "Insert fomular."
    (interactive)
    (insert "$$  $$")
    (backward-char 3))

  ;;; Some important infrastructures
  ;; org-babel settings
  (with-eval-after-load 'ob
    (require 'ob-napkin)
    (org-babel-do-load-languages
     'org-babel-load-languages
     '( (shell . t)
        (python . t)
        (emacs-lisp . t)
        (typescript . t)
        (js . t)
        (plantuml . t)
        (C . t))))

  (setq org-confirm-babel-evaluate nil)

  (defface org-headline-todo-keyword-red
    `((t :inherit error))
    "Org todo keyword red")
  (defface org-headline-todo-keyword-yellow
    '((t :inherit warning))
    "Org todo keyword red")
  (defface org-headline-todo-keyword-green
    '((t :inherit success))
    "Org todo keyword red")

  (dolist (face
           '(org-headline-todo-keyword-red
             org-headline-todo-keyword-green
             org-headline-todo-keyword-yellow))
    (set-face-font face (font-spec :family "Iosevka")))

  ;; Too much todo label is useless.
  (setq
   org-todo-keywords
   '((sequence "TODO(t)" "HACKING(h)" "|" "DONE(d)" "CANCELD(c)")
     (sequence "[!](T)" "[?](H)" "|" "[✔](D)" "[✘](C)"))
   org-todo-keyword-faces '(
                            ("TODO" . org-headline-todo-keyword-red)
                            ("[!]" . org-headline-todo-keyword-red)
                            ("HACKING" . org-headline-todo-keyword-yellow)
                            ("[?]" . org-headline-todo-keyword-yellow)
                            ("DONE" . org-headline-todo-keyword-green)
                            ("[✔]" . org-headline-todo-keyword-green)
                            ("CANCELD" . org-headline-todo-keyword-green)
                            ("[✘]" . org-headline-todo-keyword-green))
   org-priority-faces '((?A . org-headline-todo-keyword-red)
                        (?B . org-headline-todo-keyword-yellow)
                        (?C . org-headline-todo-keyword-green)))


  ;; Bullets beautify
  (use-package org-superstar
    :custom
    (org-superstar-headline-bullets-list '(?○ ?→ ?◇ ?»))
    (org-superstar-item-bullet-alist '((?* . ?●)
                                       (?+ . ?▶)
                                       (?- . ?■)))
    (org-superstar-leading-bullet "·")
    (org-superstar-leading-fallback ?​)
    (org-hide-leading-stars nil)
    :config
    (add-hook 'org-mode-hook 'org-superstar-mode :append)
    (set-face-font 'org-superstar-item (face-attribute 'variable-pitch :font))
    (set-face-font 'org-superstar-header-bullet (face-attribute 'variable-pitch :font)))

  ;; in place of ugly "..."
  (setq org-ellipsis " -> ")

  ;; Writing latex
  (setq org-highlight-latex-and-related '(native script entities))

  ;; Exporting latex
  (use-package org2ctex
    :config
    (org2ctex-toggle t)
    (add-to-list 'org2ctex-latex-packages-alist
                 "\n%%% 附加的宏包 %%%\n\\usepackage{minted}\n\\usepackage{tabu}")
    (setq org2ctex-latex-commands
          '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
            "bibtex %b"
            "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
            "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f")) ; For using minted
    )

  (setq org-latex-listings 'minted) ;; SourceCode Blocks Latex exporting
  (setq org-latex-minted-options
        '(("frame" "leftline")
          ("mathescape" "true")
          ("breaklines" "true")
          ("linenos" "false")))

  (setq org-latex-default-table-environment "tabular") ;; Org-table latex exporting


  ;; Previewing Latex
  (setq org-latex-packages-alist '(("" "fontspec" t)))
  (setq org-preview-latex-default-process 'dvisvgm)
  (setq org-preview-latex-process-alist
        '((dvisvgm :programs
                   ("xelatex" "dvisvgm")
                   :description "xdv > svg"
                   :message "you need to install the programs: xelatex and dvisvgm."
                   :use-xcolor t
                   :image-input-type "xdv"
                   :image-output-type "svg"
                   :image-size-adjust (1.7 . 1.5)
                   :latex-compiler
                   ("xelatex -no-pdf -interaction nonstopmode -output-directory %o %f")
                   :image-converter
                   ("dvisvgm %f -n -b min -c %S -o %O"))
          (imagemagick :programs
                       ("xelatex" "convert")
                       :description "pdf > png"
                       :message "you need to install the programs: xelatex and imagemagick."
                       :use-xcolor t
                       :image-input-type "pdf"
                       :image-output-type "png"
                       :image-size-adjust (1.0 . 1.0)
                       :latex-compiler
                       ("xelatex -interaction nonstopmode -output-directory %o %f")
                       :image-converter
                       ("convert -density %D -trim -antialias %f -quality 100 %O"))))

  ;; export to blog
  (use-package ox-hugo
    :after ox
    :config
    (require 'org-hugo-auto-export-mode))

  (use-package org-download
    :defer nil
    :bind
    (:map org-mode-map
          (("s-y" . org-download-yank)
           ("s-Y" . org-download-screenshot)))
    :config
    (add-hook 'dired-mode-hook 'org-download-enable) ;; Drag-and-drop to `dired`
    (setq-default org-download-image-dir (expand-file-name "../Pictures" org-directory)))


  ;; TODO: Bind these key to hydra!
  (define-key org-mode-map (kbd "C-M-;") #'colawithsauce/org-insert-formula)
  (define-key org-mode-map (kbd "C-M-:") #'colawithsauce/org-insert-inline-formula)

  ;; writeroom mode
  (use-package writeroom-mode
    :custom
    (writeroom-border-width 15))

  ;; org-drill
  (use-package org-drill
    :commands (org-drill-tree))
  )

;;; org-roam settings
(use-package org-roam
  :delight (org-roam-mode " RR")
  :hook (after-init . org-roam-mode)
  :bind  (:map org-roam-mode-map
               (("C-c n l" . org-roam)
                ("C-c n f" . org-roam-find-file)
                ("C-c n c" . org-roam-capture)
                ("C-c n d" . colawithsauce/org-roam-dailies/body))
               :map org-mode-map
               (("C-c n i" . org-roam-insert)
                ("C-c n I" . org-roam-insert-ilmmediate)
                ("C-c n t" . org-roam-tag-add)
                ("C-c n T" . org-roam-tag-delete)))
  :custom
  (org-roam-directory "~/Notes/Roam")
  (org-roam-buffer-position 'right)
  :config
  (pretty-hydra-define colawithsauce/org-roam-dailies
    (:foreign-keys
     warn
     :title "Org Roam Dailies"
     :quit-key "q")
    (
     "Move"
     (("n" org-roam-dailies-find-next-note "Next Note")
      ("p" org-roam-dailies-find-previous-note "Previous Note"))
     "Percise"
     (("d" org-roam-dailies-find-today "Today")
      ("f" org-roam-dailies-find-date "Percise Date")
      ("." org-roam-dailies-find-directory "Directory"))
     "Capture"
     (("c" org-roam-dailies-capture-today "Capture Today" :exit t)
      ("C" org-roam-dailies-capture-date "Capture Date" :exit t)
      ("t" org-roam-dailies-capture-tomorrow "Capture Tomorrow" :exit t))
     ))
  (dolist
      (ref-templates
       (list
        '("a" "Annotation" plain (function org-roam-capture--get-point)
          "%U\n#+begin_quote\n${body}\n#+end_quote"
          :file-name "${slug}"
          :head "#+title: ${title}\n#+roam_key: ${ref}\n#+roam_alias:\n"
          :immediate-finish t
          :empty-lines 1
          :unnarrowed t)
        '("f" "Full Annotation" entry (function org-roam-capture--get-point)
          "* %<%c> \n${body}"
          :file-name "${slug}"
          :head "#+title: ${title}\n#+roam_key: ${ref}\n#+roam_alias:\n"
          :immediate-finish t
          :empty-lines 1
          :unnarrowed t))
       )
    (add-to-list 'org-roam-capture-ref-templates ref-templates))
  (dolist
      (dailies-templates
       (list
        '("l" "log" entry #'org-roam-capture--get-point
          "* %<%r> %?"
          :file-name "daily/%<%Y-%m-%d>"
          :head "#+title: %<%Y-%m-%d>\n#+roam_tags: Dailies\n"
          :unnarrowed t
          :empty-lines 1)
        ;; '("i" "Introspection" entry #'org-roam-capture--get-point
        ;;   "%[~/.emacs.d/templates/Retrospection.txt]"
        ;;   :file-name "daily/%<%Y-%m-%d>"
        ;;   :head "#+title: %<%Y-%m-%d>\n#+roam_tags: Dailies\n"
        ;;   :unnarrowed t
        ;;   :empty-lines 1)
        ;; No need this item any more.
        ;; '("p" "Plan" entry #'org-roam-capture--get-point
        ;;   "%[~/.emacs.d/templates/Plan.txt]"
        ;;   :file-name "daily/%<%Y-%m-%d>"
        ;;   :head "#+title: %<%Y-%m-%d>\n#+roam_tags: Dailies\n"
        ;;   :unnarrowed t
        ;;   :empty-lines 1)
        ))
    (add-to-list 'org-roam-dailies-capture-templates
                 dailies-templates)))

(use-package org-roam-server
  :custom
  (org-roam-server-host "127.0.0.1")
  (org-roam-server-port 9090)
  (org-roam-server-export-inline-images t)
  (org-roam-server-authenticate nil)
  (org-roam-server-network-label-truncate t)
  (org-roam-server-network-label-truncate-length 60)
  (org-roam-server-network-label-wrap-length 20)
  (org-roam-server-default-exclude-filters
   (json-encode (list (list (cons 'id "Dailies") (cons 'parent "tags")))))

  :config
  (require 'org-roam-protocol)
  (unless (server-running-p)
    (org-roam-server-mode)))

(provide 'init-org)
